import ga
import re
from socket import *

class mv:

    def __init__(self, s):
        self.s = s

    def gete1(self):
        return ga.gete1(self.s)

    def gete2(self):
        return ga.gete2(self.s)

    def gete3(self):
        return ga.gete3(self.s)

# below three functions necessary for 2D hough transform
    def gete1e2(self):
        return ga.gete1e2(self.s)

    def gete2e3(self):
        return ga.gete2e3(self.s)

    def gete2ni(self):
        return ga.gete2ni(self.s)
        
    def getni(self):
        return ga.getni(self.s)

    def dual(self):
        return mv(ga.dual(self.s))

    def unit(self):
        return mv(ga.unit(self.s))

    def __mul__(self, other):
        return mv(ga.div(self.s, 1.0 / other))

    def __rmul__(self, other):
        return mv(ga.div(self.s, 1.0 / other))

    def __div__(self, other):
        return mv(ga.div(self.s, other))

    def __add__(self, other):
        if (self == 0):
          return other
        if (other == 0):
          return self
        if (isinstance(other, int) or isinstance(other, float)):
          return mv(ga.add(self.s, str(other)))
        return mv(ga.add(self.s, other.s))

    def __radd__(self, other):
        if (self == 0):
          return other
        if (other == 0):
          return self
        if (isinstance(other, int) or isinstance(other, float)):
          return mv(ga.add(self.s, str(other)))
        return mv(ga.add(self.s, other.s))

    def __or__(self, other):
        return mv(ga.lc(self.s, other.s))

    def __xor__(self, other):
        return mv(ga.op(self.s, other.s))

    def __and__(self, other):
        return mv(ga.gp(self.s, other.s))
    
    def __repr__(self):
        return self.s

    def __str__(self):
        return self.s

def vp(mv1, mv2):
    return mv(ga.vp(mv1.s, mv2.s))

class point(mv):

    def __init__(self, e1, e2, e3):
        ni = 0.5 * (e1 ** 2 + e2 ** 2 + e3 ** 2)
        mv.__init__(self, "no + {0}*e1 + {1}*e2 + {2}*e3 + {3}*ni".format(e1, e2, e3, ni))


no = mv("no")
ni = mv("ni")
e1 = mv("e1")
e2 = mv("e2")
e3 = mv("e3")

def listToMV(plist):
  mvs = []
  
  for (e1, e2, e3) in plist:
    mvs.append(point(e1, e2, e3))

  return mvs

def planeTupleToMV(tup):
  return mv("{0}*e1 + {1}*e2 + {2}*e3 + {3}*ni".format(tup[0], tup[1], tup[2], tup[3]))

def lineTupleToMV(tup):
  return mv("{0}*e1^e2 + {1}*e2^e3 + {2}*e2^ni".format(tup[0], tup[1], tup[2]))

def sendToGAV(sock, comm):
  sock.send("{0},$".format(comm))

def mvsToGAV(mvs, step):
  s = socket(AF_INET, SOCK_STREAM)
  s.connect(('localhost', 6860))
  for i in range(0, len(mvs), step):
    sendToGAV(s, "p{0} = c3ga_point({1})".format(i, mvs[i]))
  s.close()

def flatten(mvs, toe3=False):
  R = 1 + (e3 & e2)
  for i in range(0, len(mvs)):
    mvs[i] = point(mvs[i].gete1(), 0, mvs[i].gete3())
    if toe3:
      mvs[i] = vp(R, mvs[i])
      
# create dual flat point
#    mvs[i] = (e2 ^ mvs[i] ^ ni).dual() ^ e2
# undualize
#    mvs[i] = mvs[i] | (no^e1^e2^e3^ni)
# create euclidian location
#    mvs[i] = (no ^ ni) | (no ^ mvs[i])
# create c3ga point
#    mvs[i] = point(mvs[i].gete1(), mvs[i].gete2(), mvs[i].gete3())
#    mvs[i] = no + (-1 * mvs[i]) + 0.5*float((mvs[i] | mvs[i]).s.replace(" ", ""))*ni

  return mvs

def loadNN(f):
   fp = open(f, 'r')
   nn = []
   for line in fp:
       m = re.match(r"\d+ (\d+) (\d+) (\d+)", line)
       nn.append((int(m.group(1)), int(m.group(2)), int(m.group(3))))
   return nn
